// Copyright (c) 2025 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.


// 这个例程用来测试模拟开关的切换速度和ADC 输入电压的稳定时间，
// 顺便粗略测试ADC 的测量精度。
// 需要在A0 外加0V，A1 外加3.3V


#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include <LittleFS.h>
#include <Servo.h>
#include <SoftwareSerial.h>
#include <Wire.h>

// scheduler_basic
#include <scheduler_basic.hpp>

// app
#include "app_config.hpp"
#include "image_bitmap.hpp"
#include "io_expansion.hpp"
#include "oled.hpp"
#include "zou.hpp"


// =================== GLOBAL STATIC ==================

// 定时任务调度器，最大16 个任务
scheduler_basic::DelayCallback3<TimeSource, TaskCapacity> delay_callback;

// 默认的RXD 引脚被I2S 占用了，所以用软件串口发送调试信息，只占用TXD 引脚。
// 如果需要双向通讯，考虑用串口引脚重映射
SoftwareSerial serial_log{-1, PIN_TX};


// ==================  Callback TASK  ===================

TimeType task_blink() {
    serial_log.print("TaskCount = ");
    serial_log.print(delay_callback.task_count());
    serial_log.print("\n");
    ex_port2.toggle(EX_P0);
    return 800;
}



// =======================  MAIN  ========================

void setup() {
    // 软串口波特率调高到230400 时会乱码, 115200 偶尔也有乱码
    serial_log.begin(115200);
    Wire.begin(PIN_SDA, PIN_SCL);

    init_oled();
    init_servo();
    init_pcf8574();

    delay_callback.add_task(task_blink, 0);
    draw_logo();
}


int delay_us_list[] = {1000, 500, 100, 10, 1};
int us_index = 0;


void loop() {
    delay_callback.tick();

    auto us = delay_us_list[us_index];
    ++us_index;
    if(us_index == (sizeof(delay_us_list) / sizeof(int))) {
        us_index = 0;
    }

    // 用不同的延时参数测试ADC 通道切换的速度
    // 在A0 外加0V，A1 外加3.3V
    float ra0[] = {0, 0, 0};
    float ra1[] = {0, 0, 0};
    for(int i = 0; i < 3; ++i) {
        set_ad_channel(ad_channel::_0);
        delayMicroseconds(us);
        ra0[i] = read_voltage();
        set_ad_channel(ad_channel::_1);
        delayMicroseconds(us);
        ra1[i] = read_voltage();
    }

    auto list_to_string = [](float *ptr, int len) -> String {
        String s;
        for(; len > 0; --len) {
            s += *ptr;
            ++ptr;
            s += ", ";
        }
        return s;
    };

    serial_log.print("Delay Us = ");
    serial_log.println(us);
    serial_log.print("RA0 = ");
    String s = list_to_string(ra0, 3);
    serial_log.println(s);
    s = list_to_string(ra1, 3);
    serial_log.print("RA1 = ");
    serial_log.println(s);
    
    // 初步测试结果：
    // 切换通道后等待10us 就足以让ADC 采样值从0V 上升到3.3V，
    // 如果只等待1us，测量3.3V 的结果会稳定在约3.328V,
    // 略低于10us 等待的结果。
    //
    // 输入0V 时，read_voltage 稳定返回电压约34mV，
    // 输入3.29V，稳定返回3.33V
    // 所以由于测量电路或ADC 内部精度误差，测量值存在比较稳定的+34mV 偏移。
    // 
    // 用万用表实测发现，当输入0V 时，运放输出0.9mV，LMV321 的失调电压基本就是这个水平,
    // 这个电压远小于+34mV。
    // 经过衰减，在ESP8266 ADC 引脚上能测到0.18mV 电压，这个电压远小于ESP8266 的ADC 最小分辨率。
    // 所以34mV 偏移应该都是来自ESP8266 内部ADC 的误差，也有可能与电源纹波之类的有关系。
}